using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Aliyun.OSS;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Infrastructure.Configuration;
using System.Text;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// 阿里云OSS存储服务实现
    /// </summary>
    public class AliyunOssStorageService : ICloudStorageService
    {
        private readonly OssClient _ossClient;
        private readonly ILogger<AliyunOssStorageService> _logger;
        private readonly string _bucketName;
        private readonly string _endpoint;
        private readonly string _accessKeyId;
        private readonly string _accessKeySecret;
        private readonly string _baseUrl;

        public AliyunOssStorageService(EnhancedConfigurationMappingFactory configFactory, ILogger<AliyunOssStorageService> logger)
        {
            _logger = logger;
            
            var cloudConfig = configFactory.GetCloudStorageConfiguration();
            var ossConfig = cloudConfig.AliyunOss;
            
            _endpoint = ossConfig.Endpoint ?? throw new ArgumentNullException("AliyunOss:Endpoint");
            _accessKeyId = ossConfig.AccessKeyId ?? throw new ArgumentNullException("AliyunOss:AccessKeyId");
            _accessKeySecret = ossConfig.AccessKeySecret ?? throw new ArgumentNullException("AliyunOss:AccessKeySecret");
            _bucketName = ossConfig.BucketName ?? throw new ArgumentNullException("AliyunOss:BucketName");
            _baseUrl = ossConfig.BaseUrl ?? $"https://{_bucketName}.{_endpoint}";

            _ossClient = new OssClient(_endpoint, _accessKeyId, _accessKeySecret);
        }

    public Task<string> UploadAsync(Stream file, string fileName, string? folder = null, string? contentType = null)
        {
            try
            {
                var key = BuildObjectKey(folder, fileName);
                
                var request = new PutObjectRequest(_bucketName, key, file);
                
                // 设置内容类型通过元数据
                var objectMetadata = new ObjectMetadata();
                objectMetadata.ContentType = contentType ?? GetContentType(Path.GetExtension(fileName));
                request.Metadata = objectMetadata;

        // 阿里云 SDK 为同步调用，这里直接调用，避免不必要的 Task.Run 包裹
        var result = _ossClient.PutObject(request);
                
                var fileUrl = $"{_baseUrl.TrimEnd('/')}/{key}";
                
                _logger.LogInformation("文件上传到阿里云OSS成功: {FileName} -> {FileUrl}", fileName, fileUrl);
                
        return Task.FromResult(fileUrl);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "文件上传到阿里云OSS失败: {FileName}", fileName);
                throw;
            }
        }

        public async Task<string> UploadAsync(IFormFile file, string? folder = null)
        {
            if (file == null || file.Length == 0)
            {
                throw new ArgumentException("文件不能为空", nameof(file));
            }

            using var stream = file.OpenReadStream();
            return await UploadAsync(stream, file.FileName, folder, file.ContentType);
        }

        public async Task<List<string>> UploadBatchAsync(IEnumerable<IFormFile> files, string? folder = null)
        {
            var results = new List<string>();
            
            foreach (var file in files)
            {
                if (file?.Length > 0)
                {
                    try
                    {
                        var url = await UploadAsync(file, folder);
                        results.Add(url);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "批量上传中文件失败: {FileName}", file.FileName);
                        // 继续处理其他文件
                    }
                }
            }

            return results;
        }

        public Task<Stream> DownloadAsync(string fileUrl)
        {
            try
            {
                var key = ExtractObjectKeyFromUrl(fileUrl);
                var request = new GetObjectRequest(_bucketName, key);
                var result = _ossClient.GetObject(request);
                
                return Task.FromResult(result.Content);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "从阿里云OSS下载文件失败: {FileUrl}", fileUrl);
                throw;
            }
        }

        public Task<bool> DeleteAsync(string fileUrl)
        {
            try
            {
                var key = ExtractObjectKeyFromUrl(fileUrl);
                _ossClient.DeleteObject(_bucketName, key);
                
                _logger.LogInformation("从阿里云OSS删除文件成功: {FileUrl}", fileUrl);
                return Task.FromResult(true);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "从阿里云OSS删除文件失败: {FileUrl}", fileUrl);
                return Task.FromResult(false);
            }
        }

        public async Task<int> DeleteBatchAsync(IEnumerable<string> fileUrls)
        {
            var successCount = 0;
            var keys = fileUrls.Select(ExtractObjectKeyFromUrl).ToList();
            
            try
            {
                // 批量删除，最多1000个文件
                var batches = keys.Select((key, index) => new { key, index })
                    .GroupBy(x => x.index / 1000)
                    .Select(g => g.Select(x => x.key).ToList());

                foreach (var batch in batches)
                {
                    var request = new DeleteObjectsRequest(_bucketName, batch);
                    var result = _ossClient.DeleteObjects(request);
                    // 根据阿里云OSS SDK，DeleteObjects操作通常会返回删除的对象键列表
                    // 我们可以通过检查返回结果来确定成功删除的数量
                    if (result != null)
                    {
                        successCount += batch.Count; // 如果没有异常，假设批次中的所有文件都成功删除
                    }
                }
                
                _logger.LogInformation("批量删除阿里云OSS文件成功: {Count} 个", successCount);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "批量删除阿里云OSS文件失败");
                
                // 失败时逐个删除
                foreach (var fileUrl in fileUrls)
                {
                    try
                    {
                        if (await DeleteAsync(fileUrl))
                        {
                            successCount++;
                        }
                    }
                    catch (Exception deleteEx)
                    {
                        _logger.LogError(deleteEx, "单个删除阿里云OSS文件失败: {FileUrl}", fileUrl);
                    }
                }
            }

            return successCount;
        }

        public Task<bool> ExistsAsync(string fileUrl)
        {
            try
            {
                var key = ExtractObjectKeyFromUrl(fileUrl);
                var exists = _ossClient.DoesObjectExist(_bucketName, key);
                return Task.FromResult(exists);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "检查阿里云OSS文件存在性失败: {FileUrl}", fileUrl);
                return Task.FromResult(false);
            }
        }

        public Task<CloudFileInfo?> GetFileInfoAsync(string fileUrl)
        {
            try
            {
                var key = ExtractObjectKeyFromUrl(fileUrl);
                var metadata = _ossClient.GetObjectMetadata(_bucketName, key);
                
                var fileInfo = new CloudFileInfo
                {
                    FileName = Path.GetFileName(key),
                    FilePath = key,
                    Url = fileUrl,
                    Size = metadata.ContentLength,
                    LastModified = metadata.LastModified,
                    CreatedAt = metadata.LastModified, // OSS doesn't have creation time, use last modified
                    ContentType = metadata.ContentType,
                    ETag = metadata.ETag
                };

                return Task.FromResult<CloudFileInfo?>(fileInfo);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取阿里云OSS文件信息失败: {FileUrl}", fileUrl);
                return Task.FromResult<CloudFileInfo?>(null);
            }
        }

        public Task<PreSignedUploadInfo> GeneratePreSignedUploadUrlAsync(string fileName, string? folder = null, TimeSpan? expiration = null)
        {
            try
            {
                var key = BuildObjectKey(folder, fileName);
                var expiry = DateTime.UtcNow.Add(expiration ?? TimeSpan.FromHours(1));
                
                var request = new GeneratePresignedUriRequest(_bucketName, key, SignHttpMethod.Put)
                {
                    Expiration = expiry,
                    ContentType = GetContentType(Path.GetExtension(fileName))
                };
                
                var uploadUrl = _ossClient.GeneratePresignedUri(request).ToString();
                var fileUrl = $"{_baseUrl.TrimEnd('/')}/{key}";
                
                var info = new PreSignedUploadInfo
                {
                    UploadUrl = uploadUrl,
                    FileUrl = fileUrl,
                    ExpiresAt = expiry,
                    FormFields = new Dictionary<string, string>
                    {
                        { "Content-Type", request.ContentType ?? "application/octet-stream" }
                    }
                };

                return Task.FromResult(info);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成阿里云OSS预签名上传URL失败: {FileName}", fileName);
                throw;
            }
        }

        public Task<string> GeneratePreSignedDownloadUrlAsync(string fileUrl, TimeSpan? expiration = null)
        {
            try
            {
                var key = ExtractObjectKeyFromUrl(fileUrl);
                var expiry = DateTime.UtcNow.Add(expiration ?? TimeSpan.FromHours(1));
                
                var request = new GeneratePresignedUriRequest(_bucketName, key, SignHttpMethod.Get)
                {
                    Expiration = expiry
                };
                
                var downloadUrl = _ossClient.GeneratePresignedUri(request).ToString();
                
                return Task.FromResult(downloadUrl);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "生成阿里云OSS预签名下载URL失败: {FileUrl}", fileUrl);
                throw;
            }
        }

        public Task<List<CloudFileInfo>> ListFilesAsync(string? folder = null, int maxResults = 100)
        {
            try
            {
                var prefix = string.IsNullOrEmpty(folder) ? "" : $"{folder.Trim('/')}/";
                var request = new ListObjectsRequest(_bucketName)
                {
                    Prefix = prefix,
                    MaxKeys = maxResults
                };

                var result = _ossClient.ListObjects(request);
                
                var files = result.ObjectSummaries.Select(obj => new CloudFileInfo
                {
                    FileName = Path.GetFileName(obj.Key),
                    FilePath = obj.Key,
                    Url = $"{_baseUrl.TrimEnd('/')}/{obj.Key}",
                    Size = obj.Size,
                    LastModified = obj.LastModified,
                    CreatedAt = obj.LastModified,
                    ETag = obj.ETag
                }).ToList();

                return Task.FromResult(files);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "列出阿里云OSS文件失败: {Folder}", folder);
                return Task.FromResult(new List<CloudFileInfo>());
            }
        }

    public Task<string> CopyAsync(string sourceUrl, string destinationPath)
        {
            try
            {
                var sourceKey = ExtractObjectKeyFromUrl(sourceUrl);
                var destKey = destinationPath.TrimStart('/');
                
                var request = new CopyObjectRequest(_bucketName, sourceKey, _bucketName, destKey);
                _ossClient.CopyObject(request);
                
                var destUrl = $"{_baseUrl.TrimEnd('/')}/{destKey}";
                
                _logger.LogInformation("阿里云OSS文件复制成功: {Source} -> {Destination}", sourceUrl, destUrl);
                
        return Task.FromResult(destUrl);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "阿里云OSS文件复制失败: {Source} -> {Destination}", sourceUrl, destinationPath);
                throw;
            }
        }

        public async Task<string> MoveAsync(string sourceUrl, string destinationPath)
        {
            try
            {
                var destUrl = await CopyAsync(sourceUrl, destinationPath);
                await DeleteAsync(sourceUrl);
                
                _logger.LogInformation("阿里云OSS文件移动成功: {Source} -> {Destination}", sourceUrl, destUrl);
                
                return destUrl;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "阿里云OSS文件移动失败: {Source} -> {Destination}", sourceUrl, destinationPath);
                throw;
            }
        }

        public Task<StorageStatistics> GetStatisticsAsync(string? folder = null)
        {
            try
            {
                var prefix = string.IsNullOrEmpty(folder) ? "" : $"{folder.Trim('/')}/";
                var request = new ListObjectsRequest(_bucketName)
                {
                    Prefix = prefix,
                    MaxKeys = 1000 // 限制最大结果数以避免性能问题
                };

                var result = _ossClient.ListObjects(request);
                var objects = result.ObjectSummaries.ToList();
                
                var totalSize = objects.Sum(obj => obj.Size);
                
                var fileTypeStats = objects
                    .GroupBy(obj => Path.GetExtension(obj.Key).ToLowerInvariant())
                    .ToDictionary(
                        g => g.Key,
                        g => new FileTypeStats 
                        { 
                            Count = g.Count(), 
                            TotalSize = g.Sum(obj => obj.Size) 
                        });

                var stats = new StorageStatistics
                {
                    TotalFiles = objects.Count,
                    TotalSize = totalSize,
                    FileTypeStats = fileTypeStats
                };

                return Task.FromResult(stats);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取阿里云OSS存储统计失败: {Folder}", folder);
                return Task.FromResult(new StorageStatistics());
            }
        }

        private string BuildObjectKey(string? folder, string fileName)
        {
            var sanitizedFileName = SanitizeFileName(fileName);
            
            if (string.IsNullOrEmpty(folder))
            {
                return sanitizedFileName;
            }

            var sanitizedFolder = folder.Replace('\\', '/').Trim('/');
            return $"{sanitizedFolder}/{sanitizedFileName}";
        }

        private string ExtractObjectKeyFromUrl(string fileUrl)
        {
            var baseUrlPart = _baseUrl.TrimEnd('/');
            if (fileUrl.StartsWith(baseUrlPart))
            {
                return fileUrl.Substring(baseUrlPart.Length + 1);
            }
            
            // 如果不是完整URL，假设就是对象键
            return fileUrl.TrimStart('/');
        }

        private static string SanitizeFileName(string fileName)
        {
            // OSS对象键的限制
            var invalidChars = new[] { '\\', '\0', '\r', '\n', '\t' };
            var sanitized = fileName;
            
            foreach (var invalidChar in invalidChars)
            {
                sanitized = sanitized.Replace(invalidChar, '_');
            }

            // 确保不以/开头
            sanitized = sanitized.TrimStart('/');
            
            // 如果文件名为空，生成一个默认名称
            if (string.IsNullOrWhiteSpace(sanitized))
            {
                sanitized = $"file_{Guid.NewGuid():N}{Path.GetExtension(fileName)}";
            }

            return sanitized;
        }

        private static string? GetContentType(string extension)
        {
            return extension.ToLowerInvariant() switch
            {
                ".jpg" or ".jpeg" => "image/jpeg",
                ".png" => "image/png",
                ".gif" => "image/gif",
                ".webp" => "image/webp",
                ".svg" => "image/svg+xml",
                ".pdf" => "application/pdf",
                ".txt" => "text/plain",
                ".html" => "text/html",
                ".css" => "text/css",
                ".js" => "application/javascript",
                ".json" => "application/json",
                ".xml" => "application/xml",
                ".zip" => "application/zip",
                ".rar" => "application/x-rar-compressed",
                ".7z" => "application/x-7z-compressed",
                ".mp4" => "video/mp4",
                ".avi" => "video/x-msvideo",
                ".mov" => "video/quicktime",
                ".mp3" => "audio/mpeg",
                ".wav" => "audio/wav",
                ".doc" => "application/msword",
                ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ".xls" => "application/vnd.ms-excel",
                ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                ".ppt" => "application/vnd.ms-powerpoint",
                ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                _ => "application/octet-stream"
            };
        }
    }
}
